/*******************************************************************************
 ** Name: farther.c                                                           **
 ** Description: A slew of common definitions and functions.                  **
 **                                                                           **
 ** Open Source Initiative (OSI) Approved License: CDDL                       **
 **                                                                           **
 ** The contents of this file are subject to the terms of the                 **
 ** Common Development and Distribution License, Version 1.0 only             **
 ** (the "License").  You may not use this file except in compliance          **
 ** with the License.                                                         **
 **                                                                           **
 ** You can find a copy of the license in the license.txt within              **
 ** this distribution or at http://opensource.org/licenses/CDDL-1.0.          **
 ** Software distributed under the License is distributed on an "AS IS"       **
 ** basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.           **
 ** See the License for the specific language governing permissions           **
 ** and limitations under the License.                                        **
 **                                                                           **
 ** When distributing Covered Code, include this CDDL header in each          **
 ** file and include the License file at $HOME/license.txt.                   **
 ** If applicable, add the following below this header, with the indicated    **
 ** fields enclosed by brackets "[]" replaced with your own identifying       **
 ** information: Portions Copyright [yyyy] [name of copyright owner]          **
 **                                                                           **
 ** Copyright (c) 2009-2010  Barry Gian James                                 **
 ** All rights reserved.                                                      **
 **                                                                           **
 ** Ref: $HeadURL$
 ******************************************************************************/
// Portions (C) 2011-13 Open Design Strategies, LLC.
// All Rights Reserved

// $Id$
// Last Modified $DateTime$ by $Author$


#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/* msgpack code */
#define PROTOCOL_GET	0
#define PROTOCOL_SET	1
#define PROTOCOL_QUERY	2
#define PROTOCOL_REPLY	3
#define PROTOCOL_RPLEND	4
#define PROTOCOL_INT	5
#define PROTOCOL_REQ	6




/* TTY Serial base code */
/* Much code taken from snippet posted to stackoverflow.
 * See <bits/termios.h> for speed_t constants
 */
int set_interface_attribs(int fd, int speed, int parity)
{
	struct termios tty;
	memset(&tty,0,sizeof(tty));
	int it;
	if (it = tcgetattr(fd,&tty), it != 0)
	{
		printf("error %d from tcgetattr",it);
		return -1;
	}

	cfsetospeed(&tty,speed);
	cfsetispeed(&tty,speed);

	tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;	/* 8-bit chars */
	tty.c_iflag &= ~IGNBRK;	/* ignore break signal */
	tty.c_lflag = 0;	/* no signaling chars, no echo */
	tty.c_oflag = 0;	/* no remapping, no delays */
	tty.c_cc[VMIN] = 0;	/* read doesn't block */
	tty.c_cc[VTIME] = 5;	/* 0.5s read timeout */
	tty.c_iflag &= ~(IXON|IXOFF|IXANY);	/* no xon/xoff */
	tty.c_cflag |= (CLOCAL|CREAD);	/* ignore modem ctrl, ena reading */
	tty.c_cflag &= ~(PARENB|PARODD);	/* no parity by default, use passed in param */
	tty.c_cflag |= parity;
	tty.c_cflag &= ~(CSTOPB|CRTSCTS);

	if (it = tcsetattr(fd,TCSANOW, &tty), it != 0)
	{
		printf("error %d from tcsetattr",it);
		return -2;
	}

	return 0;
}

void set_blocking(int fd, int should_block)
{
	struct termios tty;
	memset(&tty,0,sizeof(tty));
	int it;
	if (it = tcgetattr(fd,&tty), it != 0)
	{
		printf("error %d from tcgetattr",it);
		return;
	}
	tty.c_cc[VMIN] = should_block ? 1 : 0;
	tty.c_cc[VTIME] = 5;
	if (it = tcsetattr(fd,TCSANOW,&tty), it != 0)
		printf("error %d in tcsetattr!",it);
}

/* reading 1 char at a time is inefficient. maybe rewrite depending on whether
 * the fd is an inet socket or a tty fd
 */
int readline_from_mcu(register int fd, register char * buf, register int sz)
{
	int n, rc;
	char c;

	for (n = 1; n < sz; n++)
	{
		if (rc = read(fd,&c,1), rc == 1)
		{
			*buf++ = c;
			if (c == '\n') break;
		} else if (rc == 0) {
			if (n == 1) return 0;	/* EOF no data */
			else break;
		} else {
			return -1;	/* some error */
		}
	}
	*buf = 0;
	return n;
}

int write_bytes_to_mcu(register int fd, register char *msg, register int sz)
{
	int nleft, nwrote;
	nleft = sz;

	while (nleft > 0)
	{
		nwrote = write(fd,msg,nleft);
		if (nwrote <= 0) return nwrote;
		nleft -= nwrote;
		msg += nwrote;
	}
	return (sz - nleft);
}

int open_tty_serial_port(char * dev, int speed, int blocking)
{
	int fd = open(dev,O_RDWR|O_NOCTTY|O_SYNC);
	if (fd < 0)
	{
		printf("error %d opening %s",fd,dev);
		return -1;
	}
	set_interface_attribs(fd,speed,0);
	set_blocking(fd,blocking);

	return fd;
}

void write_serial(register int fd, char * msg)
{
	write(fd,msg,strlen(msg));
}

int read_serial(register int fd, char * buf, int sz)
{
	return read(fd,buf,sz);
}

/* inet network base code */
/* TCP */
int open_inet_tcp(char * host, int port)
{
	int sock;
	struct sockaddr_in srv_addr;

	memset(&srv_addr,0,sizeof(srv_addr));
	srv_addr.sin_family = AF_INET;
	srv_addr.sin_addr.s_addr = inet_addr(host);
	srv_addr.sin_port = htons(port);

	if (connect(sock,(struct sockaddr*)&srv_addr,sizeof(srv_addr)) < 0)
	{
		printf("error connecting to %s on %d",host,port);
		return -1;
	}
	return sock;
}

void close_inet_tcp(register int s)
{
	close(s);
}


/* UDP */
/* TODO UDP is not fully supported and hence, not fully complete */
int send_inet_udp(char * host, int port, char * msg)
{
	struct sockaddr_in	cli_addr, srv_addr;
	int sock;

	memset(&srv_addr,0,sizeof(srv_addr));
	srv_addr.sin_family = AF_INET;
	srv_addr.sin_addr.s_addr = inet_addr(host);
	srv_addr.sin_port = htons(port);

	if (sock = socket(AF_INET, SOCK_DGRAM,0), sock < 0)
	{
		printf("error %d in udp socket()",sock);
		return -1;
	}

	memset(&cli_addr,0,sizeof(cli_addr));
	cli_addr.sin_family = AF_INET;
	cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	cli_addr.sin_port = htons(0);
	if (bind(sock,(struct sockaddr*)&cli_addr,sizeof(cli_addr)) < 0)
	{
		printf("error binding udp local socket!");
		return -2;
	}

	/* TODO */


	return 0;
}


